﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TerhelesEloszto.Common;
using GraphAlgorithm;

namespace TerhelesEloszto.Engine
{
	public class AssignmentEngine
	{
		/// <summary>
		/// A beosztás végeredményét reprezentálja.
		/// </summary>
		public class Result : IAssignmentResult
		{
			internal bool isSucceded;
			internal string reason;
			internal ITask[] fistConflict;
			internal Dictionary<int, string> assignment;

			/// <summary>
			/// Konstruktor ütköző taskokból.
			/// </summary>
			/// <param name="fc">Az ütköző taskok.</param>
			internal Result( ITask[] fc )
			{
				fistConflict = fc;
				isSucceded = false;
			}

			/// <summary>
			/// Konstruktor hibaleírásból.
			/// </summary>
			/// <param name="r">A hiba leírása.</param>
			internal Result( string r )
			{
				reason = r;
				isSucceded = false;
			}

			/// <summary>
			/// Konstruktor beosztásból
			/// </summary>
			/// <param name="a">A beosztás.</param>
			internal Result( Dictionary<int, string> a )
			{
				assignment = a;
				isSucceded = true;
			}

			#region IAssignmentResult Members

			/// <summary>
			/// Igaz, ha a beosztás sikeres volt.
			/// </summary>
			public bool IsSucceded
			{
				get { return isSucceded; }
			}

			/// <summary>
			/// Ütközö taskok esetén az első két ütköző task.
			/// </summary>
			public ITask[] FistConflict
			{
				get { return fistConflict; }
			}

			/// <summary>
			/// Egyéb hiba esetén a hiba leírása.
			/// </summary>
			public string Reason
			{
				get { return reason; }
			}

			#endregion
		}

		/// <summary>
		/// Beosztás létrehozása.
		/// </summary>
		/// <param name="currentSemester">A jelenlegi szemeszter.</param>
		/// <param name="teachers">Az oktatók.</param>
		/// <param name="tasks">A feladatok.</param>
		/// <returns>A beosztás eredménye.</returns>
		internal static Result CreateAssignment( SemesterType currentSemester, List<Teacher> teachers, List<Task> tasks )
		{
			int maxW = 0;
			int maxHours = 0;
			List<Task> currentTasks = tasks.Where( t => t.Semester == SemesterType.both || t.Semester == currentSemester ).ToList();
			foreach( Task t in currentTasks )
			{
				maxHours += t.GetHoursOnTwoAWeek();
			}
			if( maxHours == 0 ) maxHours = 1;

			if( teachers.Count == 0 ) return new Result( "Nincs oktató a rendszerben" );
			maxW = (int)( Math.Ceiling( (double)( maxHours ) / (double)( teachers.Count ) ) );
			if( maxW == 0 ) maxW = 1;

			RandomColorAlgorithm.Result result = new RandomColorAlgorithm.Result();
			result.IsSucceded = false;

			Graph G = null;
			Dictionary<int, Task> vertexTaskDictionary = null;
			Dictionary<int, Teacher> colorTeacherDictionary = null;
			int nbr = 0;

			int d = (int)( (double)( maxW ) / 20.0 );
			if( d < 1 ) d = 1;

			while( ( !result.IsSucceded ) && ( maxW <= maxHours + d ) )
			{
				G = new Graph();
				vertexTaskDictionary = new Dictionary<int, Task>();
				colorTeacherDictionary = new Dictionary<int, Teacher>();

				nbr = 0;

				// Szinek letrehozasa
				G.AddColor( teachers.Count );
				foreach( Teacher t in teachers )
				{
					colorTeacherDictionary.Add( nbr, t );
					nbr++;
				}

				nbr = 0;

				// Pontok letrehozasa.				
				foreach( Task t in currentTasks )
				{

					int w = t.GetHoursOnTwoAWeek();
					if( w == 0 ) w = 1;

					G.AddVertex( 1, w );
					vertexTaskDictionary.Add( nbr, t );

					// Szinek es pontok osszerendelese
					for( int i = 0; i < teachers.Count; i++ )
					{
						if( teachers[i].GetStars( t ) == 0 )
						{
							continue;
						}
						else if( teachers[i].GetStars( t ) == 6 && teachers[i].GetAcceptableTasks().Contains( t ) )
						{
							G.AddVertexOnlyPossibleColor( nbr, i );
							break;
						}
						else if( teachers[i].GetAcceptabeTasks( maxW, currentSemester ).Contains( t ) )
						{
							G.AddColorToVertex( nbr, i );
						}
					}

					nbr++;

				}

				// Élek lérehozása
				for( int i = 0; i < nbr; i++ )
				{
					for( int j = i + 1; j < nbr; j++ )
					{
						Task t1;
						Task t2;
						vertexTaskDictionary.TryGetValue( i, out t1 );
						vertexTaskDictionary.TryGetValue( j, out t2 );

						if( t1.HasIntersect( t2 ) )
						{
							G.AddEdge( i, j );
						}
					}
				}

				if( G.HaveVertexWithOutColor() )
				{
					maxW += d;
					continue;
				}
				G.EqualizeColorWeight();

				result = RandomColorAlgorithm.DoColoring( G, 10000 );

				maxW += d;

			}

			if( result.IsSucceded )
			{
				Dictionary<int, string> resultDictionary = new Dictionary<int, string>();

				for( int i = 0; i < nbr; i++ )
				{
					Task t;
					Teacher teacher;
					vertexTaskDictionary.TryGetValue( i, out t );
					colorTeacherDictionary.TryGetValue( G.GetVertexColor( i ), out teacher );
					resultDictionary.Add( t.ID, teacher.Neptun );

				}

				return new Result( resultDictionary );
			}
			else
			{
				if( result.FirstConflict != null )
				{
					Task[] nt = new Task[2];
					vertexTaskDictionary.TryGetValue( result.FirstConflict.Begin, out nt[0] );
					vertexTaskDictionary.TryGetValue( result.FirstConflict.End, out nt[1] );

					return new Result( nt );
				}
				else
				{
					return new Result( "Egy vagy több tárgyhoz nincs alkalmas oktató" );
				}
			}
		}

	}
}
